/*
 * Created on 30.10.2003
 */
package de.javakicker;

import java.util.*;
import java.io.*;

import org.w3c.dom.*;
import org.xml.sax.SAXException;

import javax.xml.transform.TransformerException;

/**
 * Repraesentiert ein Team in der teams.xml, und stellt ausserdem statische Methoden bereit,
 * um auf der globalen Liste aller Teams zu operieren.
 *
 * @author Till Krullmann
 */
public class Team
{
	/** Repraesentiert ein reales Teammitglied. */
	public class Member
	{
		/** Das XML-Element, in dem dieses Teammitglied gespeichert wird. */
		private Element elem;

		/**
		 * Erzeugt ein neues Teammitglied.
		 * @param elem Ein XML-Element. Dieses muss bereits bestehen und "member" als
		 *             Tag-Namen haben.
		 */
		Member(Element elem)
		{ this.elem = elem; }

		/**
		 * Liefert das darunterliegende XML-Element.
		 * @return Das XML-Element, in dem dieses Teammitglied gespeichert wird.
		 */
		Element getElement()
		{ return elem; }

		/**
		 * Liefert den Namen des Teammitglieds. Diese Eigenschaft bildet auf das Attribut "name"
		 * des "member"-Elements ab.
		 * @return Der Name des Teammitglieds.
		 */
		public synchronized String getName()
		{ return elem.getAttribute("name"); }

		/**
		 * Setzt den Namen des Teammitglieds. Diese Eigenschaft bildet auf das Attribut "name"
		 * des "member"-Elements ab.
		 * @param name Der neue Name f�r dieses Teammitglied.
		 */
		public synchronized void setName(String name)
		{ elem.setAttribute("name", name); }

		/**
		 * toString
		 */
		public String toString()
		{ return getName(); }
	}


	/**
	 * Repr�sentiert einen Spieler, also ein "virtuelles" Teammitglied. Diese Klasse steht nur f�r
	 * den Eintrag in der teams.xml Datei, darf also nicht mit den Klassen {@link Player} oder
	 * {@link Lineup.Player} verwechselt werden.
	 */
	public class Player
	{
		/** Das XML-Element, in dem dieser Spieler gespeichert wird. */
		private Element elem;

		/**
		 * Erzeugt einen neuen Spieler.
		 * @param elem Ein XML-Element. Dieses muss bereits bestehen und "player" als
		 *             Tag-Namen haben.
		 */
		Player(Element elem)
		{ this.elem = elem; }

		/**
		 * Liefert das darunterliegende XML-Element.
		 * @return Das XML-Element, in dem dieser Spieler gespeichert wird.
		 */
		Element getElement()
		{ return elem; }

		/**
		 * Liefert den Namen des Spielers. Diese Eigenschaft bildet auf das Attribut "name"
		 * des "player"-Elements ab.
		 * @return Der Name des Spielers.
		 */
		public synchronized String getName()
		{ return elem.getAttribute("name"); }

		/**
		 * Setzt den Namen des Spielers. Diese Eigenschaft bildet auf das Attribut "name"
		 * des "player"-Elements ab.
		 * @param name Der neue Name f�r diesen Spieler.
		 */
		public synchronized void setName(String name)
		{ elem.setAttribute("name", name); }

		/**
		 * Liefert den Pfad einer Bilddatei, die zur grafischen Repr�sentation dieses Spielers
		 * verwendet werden soll. Diese Eigenschaft bildet auf das Attribut "picture" des
		 * "player"-Elements ab.
		 *
		 * @return Der Pfad der Bilddatei, oder <code>null</code>, falls noch keine Bilddatei
		 *         angegeben wurde.
		 */
		public synchronized String getPictureUri()
		{ return elem.getAttribute("picture"); }

		/**
		 * Setzt den Pfad einer Bilddatei, die zur grafischen Repr�sentation dieses Spielers
		 * verwendet werden soll. Diese Eigenschaft bildet auf das Attribut "picture" des
		 * "player"-Elements ab.
		 * @param pictureUri Der Pfad einer Bilddatei, oder <code>null</code>, falls der
		 *                   bisherige Verweis gel�scht werden soll.
		 */
		public synchronized void setPictureUri(String pictureUri)
		{
			if (pictureUri == null)
				elem.removeAttribute("picture");
			else
				elem.setAttribute("picture", pictureUri);
		}

		/* (non-Javadoc)
		 * @see java.lang.Object#toString()
		 */
		public String toString()
		{ return getName(); }
	}

	/** Max. Anzahl der Spieler ({@link Player}) in einem Team */
	public static final int MAXPLAYERS = 5;

	/** Name der teams.xml Datei */
	private static final String TEAMFILE = "teams.xml";

	/** DOM-Document f�r die teams.xml */
	private static Document doc;

	/** Abbildung der Teamnamen auf die Teams. */
	private static Map<String, Team> teams = new Hashtable<String, Team>();

	/** Wurde <code>loadTeams()</code> bereits aufgerufen?. */
	private static boolean teamsLoaded = false;

	/** DOM-Elemente f�r team, members, players. */
	private Element elem, membersElem, playersElem;

	/** Sammlung aller Member-Objekte in diesem Team. */
	private Collection<Member> members = new LinkedList<Member>();

	/** Sammlung aller Player-Objekte in diesem Team. */
	private Collection<Player> players = new LinkedList<Player>();

	/** Diese Konstanten k�nnen benutzt werden, wenn ein Spiel au�erhalb der Liga
	 * ausgetragen werden soll. Das Team wird dann nicht aus der XML-Datei geladen.
	 * <code>TEAM1</code> und <code>TEAM2</code> haben unterschiedliche Namen,
	 * aber sonst keine Eigenschaften. */

	private boolean external = false;
	
	/** The external name. */
	private String externalName = null;

	/** The TEA m1. */
	public static Team TEAM1 = new Team("foo", true);
	
	/** The TEA m2. */
	public static Team TEAM2 = new Team("bar", true);

	/**
	 * L�dt und parst die Datei teams.xml. Muss nur einmal, aber vor allen anderen
	 * Methoden der Klasse <code>team</code> aufgerufen werden.
	 *
	 * @throws IOException Wenn die Datei nicht gelesen werden konnte.
	 * @throws SAXException Wenn es einen Parsing-Fehler gab.
	 */
	public static synchronized void loadTeams()
		throws IOException, SAXException
	{
		doc = XmlFile.readFromFile(TEAMFILE);

		// Name des Dokumentelements ist "teams"
		Element root = doc.getDocumentElement();

		teams.clear();

		for (Node node = root.getFirstChild(); node != null; node = node.getNextSibling())
		{
			if ( node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName() == "team" )
			{
				Element elem = (Element) node;
				Team newTeam = new Team(elem);
				teams.put(newTeam.getName(), newTeam);
			}
		}
		teamsLoaded = true;
	}

	public static synchronized void loadKickerTeams(String teamName1, String teamName2) throws IOException, SAXException {
		doc = XmlFile.create(); //XmlFile.readFromFile(TEAMFILE);
		teams.clear();
		teams.put(teamName1, new Team(teamName1));
		teams.put(teamName2, new Team(teamName2));
		teamsLoaded = true;
	}
	
	/**
	 * Schreibt die Teams in die Datei teams.xml.
	 * @throws TransformerException Wenn die Datei nicht geschrieben werden konnte.
	 */
	public static synchronized void saveTeams()
		throws TransformerException
	{
		XmlFile.writeToFile(doc, TEAMFILE);
	}

	/**
	 * Findet ein Team anhand seines Namens.
	 * @param teamName Der Name des Teams.
	 * @return Das Team mit dem angegebenen Namen, oder <code>null</code>, falls kein
	 *         solches Team existiert.
	 */
	public static Team getTeam(String teamName)
	{
		return (Team) (teams.get(teamName));
	}

	/**
	 * Liefert alle Teams, die in der teams.xml eingetragen sind.
	 * @return Ein {$link Collection}-Objekt, das eine Sammlung aller Teams enth�lt.
	 */
	public static Collection<Team> getTeams()
	{
		if (teamsLoaded)
			return teams.values();
		else
			return null;
	}

	/**
	 * Liefert alle Teams, die in der teams.xml eingetragen sind, als Abbildung; der Schl�ssel
	 * ist der Teamname.
	 * @return Ein {@link Map}-Objekt, das eine Abbildung der Teamnamen auf die Teams enth�lt.
	 */
	public static Map<String, Team> getTeamsMap()
	{
		if (teamsLoaded)
			return teams;
		else
			return null;
	}

	/**
	 * L�dt ein Team aus einem XML-Element.
	 * @param elem Ein XML-Element. Dieses muss bereits bestehen und den Namen "team" haben.
	 */
	private Team(Element elem)
	{
		this.elem = elem;

		for (Node node = elem.getFirstChild(); node != null; node = node.getNextSibling())
		{
			if (node.getNodeType() == Node.ELEMENT_NODE)
			{
				if (node.getNodeName() == "members")
				{
					membersElem = (Element) node;
					loadMembers();
				}
				else if (node.getNodeName() == "players")
				{
					playersElem = (Element) node;
					loadPlayers();
				}
			}
		}
	}

	/**
	 * Dieser Konstruktor wird nur f�r die konstanten TEAM1 und TEAM2 benutzt.
	 *
	 * @param teamName the team name
	 * @param dummyYummy the dummy yummy
	 */
	private Team(String teamName, boolean dummyYummy)
	{
		this.externalName = teamName;
		this.external = true;
	}

	/**
	 * Erzeugt ein neues Team. Dieses enth�lt keine Mitglieder und keine Spieler.
	 * @param teamName Der Name des neuen Teams.
	 */
	public Team(String teamName)
	{
		elem = doc.createElement("team");
		elem.setAttribute("name", teamName);

		membersElem = doc.createElement("members");
		elem.appendChild(membersElem);
		playersElem = doc.createElement("players");
		elem.appendChild(playersElem);

//		doc.getDocumentElement().appendChild(elem); // wirft NPE (NullPointerException)

		teams.put(teamName, this);
	}

	/** L�dt alle Mitglieder in diesem Team. */
	private void loadMembers()
	{
		members.clear();

		for (Node node = membersElem.getFirstChild(); node != null; node = node.getNextSibling())
		{
			if ( node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName() == "member" )
			{
				Member newMember = new Member( (Element) node );
				members.add(newMember);
			}
		}
	}

	/** L�dt alle Spieler in diesem Team. */
	private void loadPlayers()
	{
		players.clear();

		for (Node node = playersElem.getFirstChild(); node != null && players.size() <= MAXPLAYERS;
			node = node.getNextSibling())
		{
			if ( node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName() == "player" )
			{
				Player newPlayer = new Player( (Element) node );
				players.add(newPlayer);
			}
		}
	}

	/** L�scht das Team. */
	public void delete()
	{
		teams.remove(getName());
		doc.getDocumentElement().removeChild(elem);
	}

	/**
	 * Liefert den Namen des Teams. Diese Eigenschaft bildet auf das Attribut "name" des
	 * "team"-Objekts ab.
	 * @return Der Name des Teams.
	 */
	public synchronized String getName()
	{
		if (external)
			return externalName;
		else
			return elem.getAttribute("name");
	}

	/**
	 * Setzt den Namen des Teams. Diese Eigenschaft bildet auf das Attribut "name" des
	 * "team"-Objekts ab.
	 * @param name Der neue Name f�r das Team.
	 */
	public synchronized void setName(String name)
	{ elem.setAttribute("name", name); }

	/**
	 * Liefert alle Teammitglieder.
	 * @return Ein Array, das alle <code>Team.Member</code>-Objekte f�r dieses Team enth�lt.
	 */
	public synchronized Member[] getMembers()
	{
		Member[] membersArray = new Member[members.size()];
		return (Member[]) members.toArray(membersArray);
	}

	/**
	 * F�gt dem Team ein neues Mitglied hinzu.
	 * @param name Der Name des neuen Mitglieds.
	 * @return Ein <code>Team.Member</code>-Objekt, das das neue Teammitglied repr�sentiert.
	 */
	public synchronized Member addMember(String name)
	{
		Element newMemberElem = doc.createElement("member");
		membersElem.appendChild(newMemberElem);

		Member newMember = new Member(newMemberElem);
		newMember.setName(name);

		members.add(newMember);

		return newMember;
	}

	/**
	 * Findet ein Teammitglied anhand seines Namens.
	 * @param name Der Name des Teammitglieds.
	 * @return Das <code>Team.Member</code>-Objekt f�r das gesuchte Teammitglied, oder
	 *         <code>null</code>, falls kein solches Teammitglied existiert.
	 */
	public synchronized Member getMember(String name)
	{
		Iterator<Member> it = members.iterator();

		while (it.hasNext())
		{
			Member member = (Member) it.next();
			if (member.getName().equals(name))
				return member;
		}

		return null;
	}

	/**
	 * Entfernt ein Mitglied aus dem Team.
	 * @param member Das <code>Team.Member</code>-Objekt f�r das zu entfernende Mitglied.
	 */
	public synchronized void removeMember(Member member)
	{
		membersElem.removeChild( member.getElement() );
		members.remove(member);
	}

	/**
	 * Liefert alle Spieler.
	 * @return Ein Array, das alle <code>Team.Player</code>-Objekte f�r dieses Team enth�lt.
	 */
	public synchronized Player[] getPlayers()
	{
		Player[] playersArray = new Player[players.size()];
		return (Player[]) players.toArray(playersArray);
	}

	/**
	 * F�gt dem Team einen neuen Spieler hinzu.
	 * @param name Der Name des Spielers.
	 * @return Ein <code>Team.Player</code>-Objekt, das den neuen Spieler repr�sentiert.
	 */
	public synchronized Player addPlayer(String name)
	{
		Player newPlayer = null;

		Element newPlayerElem = doc.createElement("player");
		playersElem.appendChild(newPlayerElem);

		newPlayer = new Player(newPlayerElem);
		newPlayer.setName(name);

		players.add(newPlayer);

		return newPlayer;
	}

	/**
	 * Findet einen Spieler anhand seines Namens.
	 * @param name Der Name des Spielers.
	 * @return Das <code>Team.Player</code>-Objekt f�r den gesuchten Spieler, oder
	 *         <code>null</code>, falls kein solcher Spieler existiert.
	 */
	public synchronized Player getPlayer(String name)
	{
		Iterator<Player> it = players.iterator();
		while (it.hasNext())
		{
			Player player = (Player) it.next();
			if (player.getName().equals(name))
				return player;
		}

		return null;
	}

	/**
	 * Entfernt einen Spieler aus dem Team.
	 * @param player Das <code>Team.Player</code>-Objekt f�r den zu entfernenden Spieler.
	 */
	public synchronized void removePlayer(Player player)
	{
		playersElem.removeChild( player.getElement() );
		players.remove(player);
	}


	/**
	 * Gets the password.
	 *
	 * @return the password
	 */
	public synchronized String getPassword()
	{ return elem.getAttribute("password"); }

	/**
	 * Sets the password.
	 *
	 * @param password the new password
	 */
	public synchronized void setPassword(String password)
	{
		if (password == null)
			elem.removeAttribute("password");
		else
			elem.setAttribute("password", password);
	}


	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	public boolean equals(Object o)
	{
		return (o != null) && (o instanceof Team) &&
			((Team) o).getName().equals(this.getName());
	}


	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString()
	{
		// Dies wird f�r die Darstellung der Teams im TreeView ben�tigt
		return this.getName();
	}
}
